
/**
 * ****************************************************************************
 * A Teaching GA	Developed by Hal Stringer & Annie Wu, UCF Version 2, January
 * 18, 2004
******************************************************************************
 */
import java.io.*;
import java.util.*;
import java.text.*;

public class Search
{
    /**
     * *****************************************************************************
     * INSTANCE VARIABLES *
******************************************************************************
     */
    /**
     * *****************************************************************************
     * STATIC VARIABLES *
******************************************************************************
     */
    public static SolitaireBattleship solitaireBattleship;
    public static Individual[] member;
    public static Individual[] child;
    public static Individual bestOfGenChromo;
    public static int bestOfGenR;
    public static int bestOfGenG;
    public static Individual bestOfRunChromo;
    public static int bestOfRunR;
    public static int bestOfRunG;
    public static List<Individual> bestOverAllChromo;
    public static Individual bestOverAllChromoDelta;
    public static int bestOverAllR;
    public static int bestOverAllG;
    public static double sumRawFitness;
    public static double sumRawFitness2;	// sum of squares of fitness
    public static double sumSclFitness;
    public static double sumProFitness;
    public static double defaultBest;
    public static double defaultWorst;
    public static double averageRawFitness;
    public static double stdevRawFitness;
    public static int G;
    public static int R;
    public static Random r = new Random();
    private static double randnum;
    private static int memberIndex[];
    private static double memberFitness[];
    private static int TmemberIndex;
    private static double TmemberFitness;
    private static double fitnessStats[][];  // 0=Avg, 1=Best
    private static double runBestFitness[];  // best fitness of each run
    private static int runBestGenNum[];  // best fitness generation number
    private static double allRunBestFit[][];  // all runs best fitness
    private static double allRunAvgFit[][];  // all runs average fitness
    private static double allRunStdFit[][];  // all runs average fitness
    private static double allRunFit[][][]; // fitness of all indvs in all runs and all gens
    private static int level = 0;
    private static boolean levelSolved;
    private static int MAX_BEST = 10;

    public static void main(String[] args) throws java.io.IOException
    {
        Calendar dateAndTime = Calendar.getInstance();
        Date startTime = dateAndTime.getTime();

        //  Read Parameter File
        System.out.println("\nParameter File Name is: " + args[0] + "\n");
        Parameters parmValues = new Parameters(args[0]);
        
        String fitnessSchedule;
        if (Parameters.fitnessSchedule == 0)
            fitnessSchedule = "static";
        else if (Parameters.fitnessSchedule == 1)
            fitnessSchedule = "fixed";
        else
            fitnessSchedule = "adaptive";
        
        String filename = fitnessSchedule + Parameters.dataInputFileName;
        
        //  Write Parameters To Summary Output File
        String summaryFileName = filename + "_summary.txt";
        FileWriter summaryOutput = new FileWriter(summaryFileName);
        parmValues.outputParameters(summaryOutput);
        //  matlab output file format
        String summaryFileNameMatlab = filename + "_output.m";
        FileWriter summaryOutputMatlab = new FileWriter(summaryFileNameMatlab);

        //	Set up Fitness Statistics matrix
        fitnessStats = new double[2][Parameters.generations];
        for (int i = 0; i < Parameters.generations; i++) {
            fitnessStats[0][i] = 0;
            fitnessStats[1][i] = 0;
        }
        //	initialize all run best and average fitness matrix
        allRunAvgFit = new double[Parameters.numRuns][Parameters.generations];
        allRunStdFit = new double[Parameters.numRuns][Parameters.generations];
        allRunBestFit = new double[Parameters.numRuns][Parameters.generations];
        allRunFit = new double[Parameters.numRuns][Parameters.generations][Parameters.popSize];
        for (int i = 0; i < Parameters.numRuns; i++) {
            for (int j = 0; j < Parameters.generations; j++) {
                allRunAvgFit[i][j] = 0;
                allRunStdFit[i][j] = 0;
                allRunBestFit[i][j] = 0;
                for (int k = 0; k < Parameters.popSize; k++) {
                    allRunFit[i][j][k] = 0;
                }
            }
        }

        //	Set up Fitness Statistics matrix for best fitness of each run
        runBestFitness = new double[Parameters.numRuns];
        runBestGenNum = new int[Parameters.numRuns];
        for (int i = 0; i < Parameters.numRuns; i++) {
            runBestFitness[i] = 0;
            runBestGenNum[i] = 0;
        }

        solitaireBattleship = new SolitaireBattleship();
        
        System.out.println(solitaireBattleship.name);
        
        //	Initialize RNG, array sizes and other objects
        r.setSeed(Parameters.seed);
        memberIndex = new int[Parameters.popSize];
        memberFitness = new double[Parameters.popSize];
        member = new Individual[Parameters.popSize];
        child = new Individual[Parameters.popSize];
        bestOfGenChromo = new Individual();
        bestOfRunChromo = new Individual();
        bestOverAllChromo = new ArrayList<Individual>();
        bestOverAllChromo.add(new Individual());
        bestOverAllChromoDelta = new Individual();
        
        if (Parameters.minORmax.equals("max")) {
            defaultBest = 0;
            defaultWorst = Integer.MAX_VALUE;
        } else {
            defaultBest = Integer.MAX_VALUE;
            defaultWorst = 0;
        }

        bestOverAllChromo.get(0).rawFitness = defaultBest;
        bestOverAllChromo.get(0).dynamicFitness = defaultBest;
        bestOverAllChromoDelta.deltaFitness = Integer.MAX_VALUE;
        
        //  Start program for multiple runs
        for (R = 1; R <= Parameters.numRuns; R++) {

            levelSolved = false;
            level = 0;
            
            bestOfRunChromo.rawFitness = defaultBest;
            System.out.println();

            //	Initialize First Generation
            for (int i = 0; i < Parameters.popSize; i++) {
                member[i] = new Individual(solitaireBattleship.getDeepCopyOfShips());
                child[i] = new Individual(solitaireBattleship.getDeepCopyOfShips());
            }

            //	Begin Each Run
            for (G = 0; G < Parameters.generations; G++) {

                //Increase level based on selected fitness schedule
                switch (Parameters.fitnessSchedule)
                {
                    case 0:
                        //Static
                        level = Integer.MAX_VALUE;
                        break;
                    case 1:
                        //Fixed
                        if (G % 20 == 0)
                            level++;
                        break;
                    case 2:
                        //Adaptive
                        if (levelSolved)
                        {
                            level++;
                            levelSolved = false;
                        }
                        break;
                    default:
                        System.out.println("Invalid fitness schedule."); 
                        break;
                }
                
                sumProFitness = 0;
                sumSclFitness = 0;
                sumRawFitness = 0;
                sumRawFitness2 = 0;
                bestOfGenChromo.rawFitness = defaultBest;

                //	Test Fitness of Each Member
                for (int i = 0; i < Parameters.popSize; i++) {

                    member[i].rawFitness = 0;
                    member[i].sclFitness = 0;
                    member[i].proFitness = 0;

                    solitaireBattleship.doRawFitness(member[i], level);
                    member[i].deltaFitness = solitaireBattleship.calculateDelta(member[i]);
                    
                    if (member[i].dynamicFitness >= Board.dynamicFitnessMax)
                        levelSolved = true;
                    
                    allRunFit[R - 1][G][i] = member[i].rawFitness;

                    sumRawFitness = sumRawFitness + member[i].rawFitness;
                    sumRawFitness2 = sumRawFitness2
                            + member[i].rawFitness * member[i].rawFitness;

                    if (Parameters.minORmax.equals("max")) {
                        if (member[i].rawFitness > bestOfGenChromo.rawFitness) {
                            bestOfGenChromo = member[i].clone();
                            bestOfGenR = R;
                            bestOfGenG = G;
                        }
                        if (member[i].rawFitness > bestOfRunChromo.rawFitness) {
                            bestOfRunChromo = member[i].clone();
                            bestOfRunR = R;
                            bestOfRunG = G;
                        }
                        if (member[i].rawFitness == bestOverAllChromo.get(0).rawFitness
                        	&& bestOverAllChromo.size() < Search.MAX_BEST) {
                            boolean added = false;
                            for (int p = 0; p < bestOverAllChromo.size(); p++)
                            {
                                if (member[i].equivalent(bestOverAllChromo.get(p)))
                                {
                                    added = true;
                                }
                            }
                            
                            if (!added)
                            {
                                bestOverAllChromo.add(member[i].clone());
                            }
                        }
                        else if (member[i].rawFitness > bestOverAllChromo.get(0).rawFitness) {
                            bestOverAllChromo.clear();
                            bestOverAllChromo.add(member[i].clone());
                            bestOverAllR = R;
                            bestOverAllG = G;
                        }
                    } else {
                        if (member[i].rawFitness < bestOfGenChromo.rawFitness) {
                            bestOfGenChromo = member[i].clone();
                            bestOfGenR = R;
                            bestOfGenG = G;
                        }
                        if (member[i].rawFitness < bestOfRunChromo.rawFitness) {
                            bestOfRunChromo = member[i].clone();
                            bestOfRunR = R;
                            bestOfRunG = G;
                        }
                        if (member[i].rawFitness == bestOverAllChromo.get(0).rawFitness) {
                            boolean added = false;
                            for (int p = 0; p < bestOverAllChromo.size(); p++)
                            {
                                if (member[i].equivalent(bestOverAllChromo.get(p)))
                                {
                                    added = true;
                                }
                            }
                            
                            if (!added)
                            {
                                bestOverAllChromo.add(member[i].clone());
                            }
                        }
                        else if (member[i].rawFitness < bestOverAllChromo.get(0).rawFitness) {
                            bestOverAllChromo.clear();
                            bestOverAllChromo.add(member[i].clone());
                            bestOverAllR = R;
                            bestOverAllG = G;
                        }
                    }
                    
                    if (member[i].deltaFitness < bestOverAllChromoDelta.deltaFitness)
                    {
                    	bestOverAllChromoDelta = member[i];
                    }
                }

                // Accumulate fitness statistics
                fitnessStats[0][G] += sumRawFitness / Parameters.popSize;
                fitnessStats[1][G] += bestOfGenChromo.rawFitness;

                allRunAvgFit[R - 1][G] = sumRawFitness / Parameters.popSize;
                allRunBestFit[R - 1][G] = bestOfGenChromo.rawFitness;

                averageRawFitness = sumRawFitness / Parameters.popSize;
                stdevRawFitness = Math.sqrt(
                        Math.abs(sumRawFitness2
                        - sumRawFitness * sumRawFitness / Parameters.popSize)
                        / (Parameters.popSize - 1));

                // Output generation statistics to screen
                System.out.printf("%d\t %d\t %5.2f\t %5.2f\t %5.2f\t\n", R, G, bestOfGenChromo.rawFitness, averageRawFitness, stdevRawFitness);

                // Output generation statistics to summary file
                summaryOutput.write(" R ");
                Hwrite.right(R, 3, summaryOutput);
                summaryOutput.write(" G ");
                Hwrite.right(G, 3, summaryOutput);
                Hwrite.right((int) bestOfGenChromo.rawFitness, 7, summaryOutput);
                Hwrite.right(averageRawFitness, 11, 3, summaryOutput);
                Hwrite.right(stdevRawFitness, 11, 3, summaryOutput);
                summaryOutput.write("\n");


                // *********************************************************************
                // **************** SCALE FITNESS OF EACH MEMBER AND SUM ***************
                // *********************************************************************

                switch (Parameters.scaleType) {

                    case 0:     // No change to raw fitness
                        for (int i = 0; i < Parameters.popSize; i++) {
                            member[i].sclFitness = member[i].rawFitness + .000001;
                            sumSclFitness += member[i].sclFitness;
                        }
                        break;

                    case 1:     // Fitness not scaled.  Only inverted.
                        for (int i = 0; i < Parameters.popSize; i++) {
                            member[i].sclFitness = 1 / (member[i].rawFitness + .000001);
                            sumSclFitness += member[i].sclFitness;
                        }
                        break;

                    case 2:     // Fitness scaled by Rank (Maximizing fitness)

                        //  Copy genetic data to temp array
                        for (int i = 0; i < Parameters.popSize; i++) {
                            memberIndex[i] = i;
                            memberFitness[i] = member[i].rawFitness;
                        }
                        //  Bubble Sort the array by floating point number
                        for (int i = Parameters.popSize - 1; i > 0; i--) {
                            for (int j = 0; j < i; j++) {
                                if (memberFitness[j] > memberFitness[j + 1]) {
                                    TmemberIndex = memberIndex[j];
                                    TmemberFitness = memberFitness[j];
                                    memberIndex[j] = memberIndex[j + 1];
                                    memberFitness[j] = memberFitness[j + 1];
                                    memberIndex[j + 1] = TmemberIndex;
                                    memberFitness[j + 1] = TmemberFitness;
                                }
                            }
                        }
                        //  Copy ordered array to scale fitness fields
                        for (int i = 0; i < Parameters.popSize; i++) {
                            member[memberIndex[i]].sclFitness = i;
                            sumSclFitness += member[memberIndex[i]].sclFitness;
                        }

                        break;

                    case 3:     // Fitness scaled by Rank (minimizing fitness)

                        //  Copy genetic data to temp array
                        for (int i = 0; i < Parameters.popSize; i++) {
                            memberIndex[i] = i;
                            memberFitness[i] = member[i].rawFitness;
                        }
                        //  Bubble Sort the array by floating point number
                        for (int i = 1; i < Parameters.popSize; i++) {
                            for (int j = (Parameters.popSize - 1); j >= i; j--) {
                                if (memberFitness[j - i] < memberFitness[j]) {
                                    TmemberIndex = memberIndex[j - 1];
                                    TmemberFitness = memberFitness[j - 1];
                                    memberIndex[j - 1] = memberIndex[j];
                                    memberFitness[j - 1] = memberFitness[j];
                                    memberIndex[j] = TmemberIndex;
                                    memberFitness[j] = TmemberFitness;
                                }
                            }
                        }
                        //  Copy array order to scale fitness fields
                        for (int i = 0; i < Parameters.popSize; i++) {
                            member[memberIndex[i]].sclFitness = i;
                            sumSclFitness += member[memberIndex[i]].sclFitness;
                        }

                        break;

                    default:
                        System.out.println("ERROR - No scaling method selected");
                }


                // *********************************************************************
                // ****** PROPORTIONALIZE SCALED FITNESS FOR EACH MEMBER AND SUM *******
                // *********************************************************************

                for (int i = 0; i < Parameters.popSize; i++) {
                    member[i].proFitness = member[i].sclFitness / sumSclFitness;
                    sumProFitness = sumProFitness + member[i].proFitness;
                }

                // *********************************************************************
                // ************ CROSSOVER AND CREATE NEXT GENERATION *******************
                // *********************************************************************

                int parent1 = -1;
                int parent2 = -1;

                //  Assumes always two offspring per mating
                for (int i = 0; i < Parameters.popSize; i = i + 2) {

                    //	Select Two Parents
                    parent1 = Individual.selectParent();
                    parent2 = parent1;
                    while (parent2 == parent1) {
                        parent2 = Individual.selectParent();
                    }

                    //	Crossover Two Parents to Create Two Children
                    randnum = r.nextDouble();
                    if (randnum < Parameters.xoverRate) {
                        Individual.mateParents(parent1, parent2, member[parent1], member[parent2], child[i], child[i + 1], level);
                    } else {
                        Individual.mateParents(parent1, member[parent1], child[i]);
                        Individual.mateParents(parent2, member[parent2], child[i + 1]);
                    }
                } // End Crossover

                //	Mutate Children
                for (int i = 0; i < Parameters.popSize; i++) {
                    child[i].doMutation();
                }

                //	Swap Children with Last Generation
                for (int i = 0; i < Parameters.popSize; i++) {
                    member[i] = child[i].clone();
                }

                double c_std = 0;
                for (int i_s = 0; i_s < Parameters.popSize; i_s++) {
                    double subt = allRunAvgFit[R - 1][G] - allRunFit[R - 1][G][i_s];
                    c_std += java.lang.Math.pow(subt, 2);
                }
                c_std = c_std / Parameters.popSize;
                allRunStdFit[R - 1][G] = java.lang.Math.sqrt(c_std);


                //Print best of generation
                //solitaireBattleship.printIndividual(bestOfGenChromo);


            } //  Repeat the above loop for each generation


            Hwrite.left(bestOfRunR, 4, summaryOutput);
            Hwrite.right(bestOfRunG, 4, summaryOutput);

            System.out.println(R + "\t" + "B" + "\t" + bestOfRunChromo.rawFitness);
            runBestFitness[R - 1] = bestOfRunChromo.rawFitness;
            runBestGenNum[R - 1] = bestOfRunG;

            //Print best of run
            //solitaireBattleship.printIndividual(bestOfRunChromo);

        } //End of a Run

        Hwrite.left("B", 8, summaryOutput);
       

        //	Output Fitness Statistics matrix
        summaryOutput.write("Gen                 AvgFit              BestFit \n");
        for (int i = 0; i < Parameters.generations; i++) {
            Hwrite.left(i, 15, summaryOutput);
            Hwrite.left(fitnessStats[0][i] / Parameters.numRuns, 20, 2, summaryOutput);
            Hwrite.left(fitnessStats[1][i] / Parameters.numRuns, 20, 2, summaryOutput);
            summaryOutput.write("\n");
        }

        //Calculate delta between best overall and optimal solution
        int bestFitDelta = solitaireBattleship.calculateDelta(bestOverAllChromo.get(0));        
        summaryOutput.write("---Best overall (" + bestOverAllChromo.size() + ") " + bestOverAllChromo.get(0).rawFitness);
        
        //Print best overall
        boolean enableCanvas = true;
        
        System.out.println();
        System.out.print("---Best overall raw fitness. Found: " + bestOverAllChromo.size() + " Raw fit: " + bestOverAllChromo.get(0).rawFitness + " Delta: " + bestFitDelta);
        for (Individual i: bestOverAllChromo)
        {
            solitaireBattleship.printIndividual(i);
        }
        
        System.out.print("---Best overall delta fitness. Raw fit: " + bestOverAllChromoDelta.rawFitness + " Delta: " + bestOverAllChromoDelta.deltaFitness);
        solitaireBattleship.printIndividual(bestOverAllChromoDelta);
        
        if (enableCanvas)
        {
        	boolean enableAutoClose = false;
        	
            SolitaireBattleshipCanvas canvasDelta = new SolitaireBattleshipCanvas(solitaireBattleship.board, 4, "Best Overall Chromo Delta");
            canvasDelta.setBoardResult(new BoardResult(bestOverAllChromoDelta.getShips(), solitaireBattleship.board, Integer.MAX_VALUE));

            //canvasDelta.saveToFile("delta.jpeg");

            if (enableAutoClose)
                canvasDelta.close();
            
            for (int i = 0; i < bestOverAllChromo.size(); i++)
            {
                SolitaireBattleshipCanvas canvasFit = new SolitaireBattleshipCanvas(solitaireBattleship.board, 4, "Best Overall Chromo Fit (" + (i+1) + " of " + bestOverAllChromo.size() + ")");
                canvasFit.setBoardResult(new BoardResult(bestOverAllChromo.get(i).getShips(), solitaireBattleship.board, Integer.MAX_VALUE));
                
                //Stop at 3 if we are not at max fitness
                if (i == 3 && bestOverAllChromo.get(0).rawFitness != Board.MAX_FITNESS)
                    break;
                
                //canvasFit.saveToFile("fit.jpeg");
                
                if (enableAutoClose)
                    canvasFit.close();
            }
        }
        //System.out.print("---Optimal solution" + solitaireBattleship.optimalIndividual.rawFitness);
        //solitaireBattleship.printIndividual(solitaireBattleship.optimalIndividual);
        
        summaryOutput.write("\n");
        summaryOutput.close();

        // write average fitness in matlab file
        summaryOutputMatlab.write("clear all; clc; close all;\n");
        summaryOutputMatlab.write("avgFit=[");
        for (int i = 0; i < Parameters.generations; i++) {
            Hwrite.left(fitnessStats[0][i] / Parameters.numRuns, 20, 2, summaryOutputMatlab);
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");

        summaryOutputMatlab.write("bestFit=[");
        for (int i = 0; i < Parameters.generations; i++) {
            Hwrite.left(fitnessStats[1][i] / Parameters.numRuns, 20, 2, summaryOutputMatlab);
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");

        summaryOutputMatlab.write("bestEachRunFit=[");
        for (int i = 0; i < Parameters.numRuns; i++) {
            Hwrite.left(runBestFitness[i], 20, 2, summaryOutputMatlab);
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");

        summaryOutputMatlab.write("bestEachRunGen=[");
        for (int i = 0; i < Parameters.numRuns; i++) {
            Hwrite.left(runBestGenNum[i], 20, 2, summaryOutputMatlab);
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");

        summaryOutputMatlab.write("allRunBestFit=[");
        for (int i = 0; i < Parameters.numRuns; i++) {
            for (int j = 0; j < Parameters.generations; j++) {
                Hwrite.left(allRunBestFit[i][j], 20, 2, summaryOutputMatlab);
                summaryOutputMatlab.write(",");
            }
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");

        summaryOutputMatlab.write("allRunAvgFit=[");
        for (int i = 0; i < Parameters.numRuns; i++) {
            for (int j = 0; j < Parameters.generations; j++) {
                Hwrite.left(allRunAvgFit[i][j], 20, 2, summaryOutputMatlab);
                summaryOutputMatlab.write(",");
            }
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");
        summaryOutputMatlab.write("\n");

        summaryOutputMatlab.write("allRunStdFit=[");
        for (int i = 0; i < Parameters.numRuns; i++) {
            for (int j = 0; j < Parameters.generations; j++) {
                Hwrite.left(allRunStdFit[i][j], 20, 2, summaryOutputMatlab);
                summaryOutputMatlab.write(",");
            }
            summaryOutputMatlab.write("\n");
        }
        summaryOutputMatlab.write("];\n");
        summaryOutputMatlab.write("\n");

//		String str_t = "allRunFit=zeros(" + Parameters.numRuns + "," + Parameters.generations + "," + Parameters.popSize + ");\n";
//		summaryOutputMatlab.write(str_t);
//		for (int i=0; i<Parameters.numRuns; i++){
//			for (int j=0; j<Parameters.generations; j++){
//				for (int k=0; k<Parameters.popSize; k++){
//					str_t = "allRunFit(" + (i+1) + "," + (j+1) + "," + (k+1) + ")=" + allRunFit[i][j][k] + ";\n";
//					summaryOutputMatlab.write(str_t);
//				}
//			}
//		}
//		summaryOutputMatlab.write("\n");


        summaryOutputMatlab.write("figure(1);\n");
        summaryOutputMatlab.write("plot(mean(allRunAvgFit), 'color', 'r', 'linewidth', 2);\n");
        summaryOutputMatlab.write("hold on;\n");
        summaryOutputMatlab.write("plot(mean(allRunBestFit), 'color', 'b', 'linewidth', 2);\n");
        summaryOutputMatlab.write("legend('avg avg fit','avg best fit', 'Location', 'NorthWest');\n");
        summaryOutputMatlab.write("xlabel('Generations');\n");
        summaryOutputMatlab.write("ylabel('Fitness');\n");
        summaryOutputMatlab.write("hold off;\n");
        summaryOutputMatlab.write("plotf = getframe(gcf);\n");
        summaryOutputMatlab.write("imwrite(plotf.cdata,'" + filename + ".tif', 'tiff');\n");
        summaryOutputMatlab.write("\n");

        summaryOutputMatlab.write("figure(2);\n");
        summaryOutputMatlab.write("plot(std(allRunAvgFit), 'color', 'r', 'linewidth', 2);\n");
        summaryOutputMatlab.write("hold on;\n");
        summaryOutputMatlab.write("plot(std(allRunBestFit), 'color', 'b', 'linewidth', 2);\n");
        summaryOutputMatlab.write("legend('std avg fit','std best fit', 'Location', 'NorthWest');\n");
        summaryOutputMatlab.write("xlabel('Generations');\n");
        summaryOutputMatlab.write("ylabel('Standard Deviation');\n");
        summaryOutputMatlab.write("hold off;\n");
        summaryOutputMatlab.write("plotf = getframe(gcf);\n");
        summaryOutputMatlab.write("imwrite(plotf.cdata,'" + filename + "2.tif', 'tiff');\n");
        summaryOutputMatlab.write("\n");


        summaryOutputMatlab.write("[muhat,sigmahat,muci,sigmaci] = normfit(bestEachRunFit);\n");
        summaryOutputMatlab.write("display(['mean best fit: ' num2str(muhat)]);\n");
        summaryOutputMatlab.write("display(['std best fit: ' num2str(sigmahat)]);\n");
        summaryOutputMatlab.write("display(['95% CI mean best fit: (' num2str(muci(1)) ',' num2str(muci(2)) ')']);\n");
        summaryOutputMatlab.write("display(['95% CI std best fit: (' num2str(sigmaci(1)) ',' num2str(sigmaci(2)) ')']);\n");
        summaryOutputMatlab.write("clear muhat sigmahat muci sigmaci;\n");

        summaryOutputMatlab.write("[muhat,sigmahat,muci,sigmaci] = normfit(bestEachRunGen);\n");
        summaryOutputMatlab.write("display(['mean best fit gen: ' num2str(muhat)]);\n");
        summaryOutputMatlab.write("display(['std best fit gen: ' num2str(sigmahat)]);\n");
        summaryOutputMatlab.write("display(['95% CI mean best fit gen: (' num2str(muci(1)) ',' num2str(muci(2)) ')']);\n");
        summaryOutputMatlab.write("display(['95% CI std best fit gen: (' num2str(sigmaci(1)) ',' num2str(sigmaci(2)) ')']);\n");
        summaryOutputMatlab.write("clear muhat sigmahat muci sigmaci;\n");

        summaryOutputMatlab.write("[muhat,sigmahat,muci,sigmaci] = normfit(bestEachRunGen(bestEachRunFit>.9999999));\n");
        summaryOutputMatlab.write("display(['mean optimum gen: ' num2str(muhat)]);\n");
        summaryOutputMatlab.write("display(['std optimum gen: ' num2str(sigmahat)]);\n");
        summaryOutputMatlab.write("display(['95% CI mean optimum gen: (' num2str(muci(1)) ',' num2str(muci(2)) ')']);\n");
        summaryOutputMatlab.write("display(['95% CI std optimum gen: (' num2str(sigmaci(1)) ',' num2str(sigmaci(2)) ')']);\n");
        summaryOutputMatlab.write("clear muhat sigmahat muci sigmaci;\n");
        
        
        bestFitDelta = solitaireBattleship.calculateDelta(bestOverAllChromo.get(0));
        summaryOutputMatlab.write("display('---Best overall (" + bestOverAllChromo.size() + ") " + bestOverAllChromo.get(0).rawFitness + "');\n");
        summaryOutputMatlab.write("display('---Best overall raw fitness. Found: " + bestOverAllChromo.size() + " Raw fit: " + bestOverAllChromo.get(0).rawFitness + " Delta: " + bestFitDelta + "');\n");
        summaryOutputMatlab.write("display('---Best overall delta fitness. Raw fit: " + bestOverAllChromoDelta.rawFitness + " Delta: " + bestOverAllChromoDelta.deltaFitness + "');\n");
        
        summaryOutputMatlab.close();

        System.out.println();
        System.out.println("Start:  " + startTime);
        dateAndTime = Calendar.getInstance();
        Date endTime = dateAndTime.getTime();
        System.out.println("End  :  " + endTime);

    }
}